=begin
zip archive reader
=end

require "rubygems"
require "zip/zip"
require "tempfile"
require "fileutils"
require "kconv"
require "thread"
require "logger"
require "singleton"

module RCV
  class ZipReader
    def initialize(file)
      @logger = RCVLog.instance
      @zis = Zip::ZipInputStream.new(file)
      @entries = []
      @index = 0
    end

    def read
      while entry = @zis.get_next_entry
        case entry.name
        when /\.zip$/i
          @entries << ZipEntry.new(entry)
        when /\.(jpg|jpeg|png)$/i
          @entries << ImageEntry.new(entry)
        end
      end
      @entries = @entries.sort_by { |e| e.name }
    end

    def close
      @entries.each { |e| e.close }
      @zis.close
    end

    def get_next
      entry = current
      unless entry
        @index -= 1
        entry = current
      end
      if entry.last?
        entry.close
        @index += 1
        entry = current
      end
      return unless entry
      entry.get_next
    end

    def get_prev
      entry = current
      unless entry
        @index -= 1
        entry = current
      end
      if @index == 0
        return if entry.top?
      end
      if entry.top?
        entry.close
        @index -= 1
        entry = current
      end
      return unless entry
      entry.get_prev
    end

    def state
      state = [@index, current.state].flatten
      @logger.debug "Get state : #{state.join(",")}"
      state
    end

    def load(state)
      @logger.debug "Set state : #{state.join(",")}"
      @index = state.shift
      current.load state
    end

    def current
      @entries[@index]
    end
  end

  class Entry
    attr_reader :name
    def initialize(entry)
      @logger = RCVLog.instance
      @entry = entry
      @name = entry.name.toutf8
      @index = -1
    end

    def top?
      @index <= 0
    end

    def close; end
  end

  # ignore nested too deep
  class ZipEntry < Entry
    def initialize(entry)
      super
      @entries = []
    end

    # No goodness?
    # tempfile or memory ? best ?
    def get_next
      unless @zis
        create_temp
      end
      if @temp.closed?
        @temp.open
      end
      @entries[@index].close if @entries[@index]
      @index += 1
      @entries[@index].get_next
    end

    def get_prev
      unless @zis
        create_temp
      end
      if @temp.closed?
        @index = @entries.size
        @temp.open
      end
      @entries[@index].close if @entries[@index]
      @index -= 1
      @entries[@index].get_prev
    end

    def create_temp
      @index = -1
      @temp = Tempfile.new("rcv")
      @temp.binmode
      @temp.write @entry.get_input_stream.read
      @temp.flush
      @logger.debug "Create temp : #{@name} to #{@temp.path}"
      @zis = Zip::ZipInputStream.new(@temp.path)
      while entry = @zis.get_next_entry
        @entries << ImageEntry.new(entry) if entry.name =~ /\.(jpg|jpeg|png)$/i
      end
      @entries = @entries.sort_by { |e| e.name }
    end

    def close
      @temp.close
      @entries[@index].close
#       @zis.close
#       @zis = nil
#       @temp.close(true)
    end

    def last?
      create_temp unless @zis
      @index + 1 >= @entries.size
    end

    def state
      [@index - 1, @entries[@index].state]
    end

    def current
      @entries[@index]
    end

    def load(state)
      @index = state.shift
      current.load state
    end
  end

  class ImageEntry < Entry
    def initialize(entry)
      super
    end

    def get_next
      current
    end

    def get_prev
      current
    end

    def current
      @index = 0
      @logger.debug "Read file : #{@name}"

      unless @temp
        @temp = Tempfile.new("rcv")
        @temp.binmode
        @temp.write(@entry.get_input_stream.read)
        @temp.flush
        @logger.debug "Create temp : #{@temp.path}"
      end
      if @temp.closed?
        @temp.open
      end

      @temp.path
    end

    def last?
      @index == 0
    end

    def close
      @logger.debug "close #{name}"
      @temp.close(true) if @temp
      @temp = nil
    end

    def state
      @index - 1
    end

    def load(state)
      @index = state.shift
    end
  end

  class RCVLog
    include Singleton
    def initialize
      @logger = Logger.new(STDOUT)
      @logger.level = Logger::ERROR
      @m = Mutex.new
    end

    def debug str
      @m.synchronize do
        @logger.debug str
      end
    end

    def error str
      @m.synchronize do
        @logger.error str
      end
    end
  end
end
